"""
 Wrapper class for collective2 signal entry API.
 For details, http://www.collective2.com/content/automation.htm
 
 Created on May, 2012
 @author: Xiaoyuan Xu
"""

import urllib
from xml.dom.minidom import parseString

  
"""
XML parser for parsing the xml response 
"""
class XMLParser:
    # tag name list
    TAG_SIGNAL = "signalid"
    TAG_STATUS = "status"
          
    @staticmethod
    def get_element_by_tagname(xml, tagName, index=0):
        dom = parseString(xml)
        try:
            xmlTag = dom.getElementsByTagName(tagName)[index].toxml()
            xmlData=xmlTag.replace('<'+tagName+'>','').replace('</'+tagName+'>','')
        except IndexError:
            print "tagName: " + tagName + " doesn't exist!"
            raise
        return xmlData
    
    @staticmethod
    def get_signalid(xml, ID=""):
        try:
            signalid = XMLParser.get_element_by_tagname(xml, XMLParser.TAG_SIGNAL + ID)
        except:
            print "Fail to get signal id!"
            signalid = -1
        return signalid
    
    @staticmethod
    def get_status(xml):
        try:
            status = XMLParser.get_element_by_tagname(xml, XMLParser.TAG_STATUS)
        except:
            print "Fail to get signal id!"
            status = "error"
        return status

    
class Collective2:
    
    # constant string list
    BASEURL = "http://www.collective2.com/cgi-perl/signal.mpl?"
    CMD_SIGNAL = "signal"
    CMD_REVERSE = "reverse"
    CMD_CANCEL = "cancel"
    CMD_CANCEL_ALL = "cancelallpending"
    
    BUY_TO_OPEN = "BTO"       # open a long position
    SELL_SHORT = "SSHORT"     # used for stocks
    SELL_TO_OPEN = "STO"      # used for non-stocks
    BUY_TO_CLOSE = "BTC"      # close a short position
    SELL_TO_CLOSE = "STC"     # close a long position
    
    STOCK = "stock"
    OPTION = "option"
    FUTURE = "future"
    FOREX = "forex"
          
    DAY = "DAY"             # Day Order      
    GTC = "GTC"             # Good Til Cancel
    
    # parameter list
    PAR_CMD = "cmd"
    PAR_SYSTEMID = "systemid"    # The system ID displayed as above.      
    PAR_PW = "pw"                # login paseword, case sensitive
    PAR_ACTION = "action"        # BTO/SSHORT/STO/BTC/STC
    PAR_QUANT = "quant"          # Number of shares or contracts
    PAR_DOLLAR = "dollars"
    PAR_INSTRUMENT = "instrument"    #stock     
    PAR_SYMBOL = "symbol"
    PAR_LIMIT = "limit"
    PAR_STOP = "stop"
    PAR_DURATION = "duration"
    PAR_C2EMAIL = "c2email"
    PAR_CONDUPON = "conditionalupon"  # for conditional order
    PAR_TRIGGERPRICE = "triggerprice"
    PAR_SIGNALID = "signalid"

    def __init__(self, email, pw):
        self.email = email
        self.pw = pw
        
    def __request(self, parameters):
        try:
            url = self.BASEURL + "pw=" + self.pw 
            for pair in parameters:
                if pair[0] != "" and pair[1] != "":
                    url += "&" + str(pair[0]) + "=" + str(pair[1])                            
            
            response = urllib.urlopen(url).read().strip()
            print url
            if XMLParser.get_status(response) == "error":
                print "Request failed! " + response
            else:
                print "Request Succeeded! " + XMLParser.get_status(response)                       
            return response      
        except IOError:
            print "Can't connect to the server: " + url
            raise
        except BaseException as excep:
            print("Unknown Error in Collective2.__request " + 
                  str(url) + " with exception " + str(excep))
            raise
    
    def __transac(self, systemid_="",  symbol_="", action_="", quant_="",
                  instrument_="", duration_=DAY, limit_="", stop_="", dollars_="", 
                  condupon_="", cmd_=CMD_SIGNAL, triggerprice_="", signalid_="" ): 
        try: 
            parameters = [[self.PAR_CMD, cmd_],
                         [self.PAR_SYSTEMID, systemid_],
                         [self.PAR_ACTION, action_],
                         [self.PAR_QUANT, quant_],
                         [self.PAR_DOLLAR, dollars_],
                         [self.PAR_INSTRUMENT, instrument_],
                         [self.PAR_SYMBOL, symbol_],
                         [self.PAR_LIMIT, limit_],
                         [self.PAR_STOP, stop_],
                         [self.PAR_DURATION, duration_],
                         [self.PAR_CONDUPON, condupon_],
                         [self.PAR_TRIGGERPRICE, triggerprice_],
                         [self.PAR_SIGNALID, signalid_]
                         ]
                
            response = self.__request(parameters)
        except:
            print "Fail to send transac!"
            raise
            
        return response
      
    def place_order(self, systemid, symbol, action, quant, instrument,
                    duration=DAY, limit="", stop="", dollars="", condupon="",
                    cmd=CMD_SIGNAL):
        response = self.__transac(systemid_=systemid, symbol_=symbol,
                                  action_=action, quant_=quant, 
                                  instrument_=instrument, duration_=duration,
                                  limit_=limit, stop_=stop, dollars_=dollars,
                                  condupon_=condupon, cmd_=cmd)
        return XMLParser.get_signalid(response)
        
    def reverse_order(self, systemid, symbol, quant="", duration=DAY, 
                      triggerprice=""):
        response = self.__transac(systemid_=systemid, quant_=quant, symbol_=symbol,
                                  duration_=duration, cmd_=self.CMD_REVERSE)
        return [XMLParser.get_signalid(response,str(1)), 
                XMLParser.get_signalid(response, str(2))]
    
    def cancel_order(self, systemid, signalid = ""):
        if signalid == "":
            self.__transac(systemid_=systemid, cmd_=self.CMD_CANCEL_ALL)
        else:
            self.__transac(systemid_=systemid, cmd_=self.CMD_CANCEL, signalid_=signalid)
            
    
# Example:
#    mySystemId = "73627960"
#    email = ""
#    pw = ""
#    
#    c2 = Collective2(email,pw)
#    print "buy 1200 shares of IBM at the market."
#    signalid = c2.place_order(mySystemId, action=BUY_TO_OPEN, quant=20, instrument=STOCK, symbol="IBM", duration=DAY)
